import os
import subprocess
from docker import APIClient
from datetime import datetime
from ..models import deployInfo
from .update_log import update_log
from images.models import imageInfo
from systemd.models import registryInfo
from applications.utils.application_entrance import Create_Update_Application


class DeployBuildApplications:
    # 项目构建
    def __init__(self, app_info, script_path=None, *args, **kwargs):
        self.app_info = app_info
        self.logs_path = f'/opt/data/logs/{self.app_info["env"]}'
        self.script_path = script_path
        self.image_tag = datetime.now().strftime("%Y%m%d%H%M%S")
        # self.C = APIClient(base_url='unix:///var/run/docker.sock', timeout=60)
        self.C = APIClient(base_url='http://192.168.10.204:2375', timeout=60)

    def run_shell_script(self):
        # 设置系统环境变量
        if os.path.exists(self.logs_path) is False:
            os.makedirs(self.logs_path)
        else:
            logfile_path = os.path.join(self.logs_path, f"{self.app_info['name']}.log")
            if os.path.exists(logfile_path) is True:
                os.remove(logfile_path)
        for key, value in self.app_info.items():
            try:
                os.environ[key] = str(value)
            except Exception as e:
                message, status, error_info = "发布失败", False, f"[ERROR] 声明环境变量出错，str{e}\n"
                return update_log(projectName=self.app_info['name'],
                                  projectType=self.app_info['application_type'],
                                  status=status, code=500,
                                  clusterName=self.app_info['env'],
                                  message=message).update_dtalk(log_path=self.logs_path, log_info=error_info)
        os.environ['tag'] = self.image_tag
        os.environ['app_path'] = f"/opt/data/projectData/{self.app_info['env']}"
        os.environ['backPackage'] = f"/opt/data/backPackage/{self.app_info['env']}"
        os.environ['deployPackage'] = f"/opt/data/deployPackage/{self.app_info['env']}"

        regsitry_instance = registryInfo.objects.filter(env=self.app_info['env']).values().first()
        if regsitry_instance is None or len(regsitry_instance) <= 0:
            message, status, error_info = "发布失败", False, "[ERROR] 获取不到当前环境对应的仓库信息\n"
            return update_log(projectName=self.app_info['name'],
                              projectType=self.app_info['application_type'],
                              status=status, code=500,
                              clusterName=self.app_info['env'],
                              message=message).update_dtalk(log_path=self.logs_path, log_info=error_info)

        if os.path.exists(self.app_info.get('script_path')) is False:
            message, status, error_info = "发布失败", False, f"[ERROR] 没有找到指定路径的文件，{self.app_info.get('script_path')}\n"
            return update_log(projectName=self.app_info['name'],
                              projectType=self.app_info['application_type'],
                              status=status, code=500,
                              clusterName=self.app_info['env'],
                              message=message).update_dtalk(log_path=self.logs_path, log_info=error_info)
        else:
            # 执行项目构建shell脚本
            script_running_status = subprocess.Popen(
                f"sh {self.app_info.get('script_path')} > {self.logs_path}/{self.app_info.get('name')}.log", shell=True,
                stdout=subprocess.PIPE).wait()

        if script_running_status != 0:
            message, status, error_info = "发布失败", False, "[ERROR] 服务在构建过程中发生未知错误\n"
            return update_log(projectName=self.app_info['name'],
                              projectType=self.app_info['application_type'],
                              status=status, code=500,
                              clusterName=self.app_info['env'],
                              message=message).update_dtalk(log_path=self.logs_path, log_info=error_info)
        else:
            return self.run_build_docker_image()

    def run_build_docker_image(self):
        # 构建、 推送docker镜像
        regsitry_instance = registryInfo.objects.filter(env=self.app_info['env']).values().first()
        image = f"{regsitry_instance['registry']}/{regsitry_instance['namespace']}/{self.app_info['name']}"
        subprocess.call(
            f"docker build -t {image}:{self.image_tag} $deployPackage/$name --build-arg application=$name >> {self.logs_path}/{self.app_info['name']}.log",
            shell=True, stdout=True)

        # 扫描本地镜像并存储为同一集合，用于判断镜像是否构建成功.
        images = [image for images in self.C.images() if images.get('RepoTags') is not None for image in
                  images.get('RepoTags')]
        if f"{image}:{self.image_tag}" not in images:
            message, status, error_info = "镜像构建失败", False, '[ERROR] 镜像构建失败!\n'
            return update_log(projectName=self.app_info['name'],
                              projectType=self.app_info['application_type'],
                              status=status, code=500,
                              clusterName=self.app_info['env'],
                              message=message).update_dtalk(log_path=self.logs_path, log_info=error_info)
        else:
            with open(f"{self.logs_path}/{self.app_info['name']}.log", 'a+') as f:
                f.write('[INFO] 镜像构建成功\n[INFO] 开始推送镜像...\n')
        try:
            self.C.login(username=regsitry_instance["username"], password=regsitry_instance["password"],
                         registry=regsitry_instance["registry"])
            # 推送镜像
            for line in self.C.push(repository=image, tag=self.image_tag, stream=True, decode=True):
                for key, value in line.items():
                    if key != 'id' and len(value) > 0 and key != 'aux':
                        with open('push.log', 'a+') as f:
                            f.write(f"{value}\n")
        except Exception as e:
            message, status, error_info = "镜像推送失败", False, f"[ERROR] 镜像推送失败，{str(e)}\n'"
            return update_log(projectName=self.app_info['name'],
                              projectType=self.app_info['application_type'],
                              status=status, code=500,
                              clusterName=self.app_info['env'],
                              message=message).update_dtalk(log_path=self.logs_path, log_info=error_info)
            # raise e
        else:
            with open(f"{self.logs_path}/{self.app_info['name']}.log", 'a+') as f:
                f.write('[INFO] 镜像推送成功!\n[INFO] 开始部署到集群...\n')
            return self.transfer_k8s_api(image=image)

    def transfer_k8s_api(self, image):
        # 调用kubernetes接口启动服务
        try:
            cluster_oprate = Create_Update_Application()
            cluster_oprate.entrance(tag=self.image_tag,
                                    namespace=self.app_info['env'],
                                    port=self.app_info['port'],
                                    application_name=self.app_info['name'])
        except Exception as e:
            message, status, error_info = "kubernetes接口调用失败", False, f"[ERROR] kubernetes接口调用失败,{str(e)}\n'"
            return update_log(projectName=self.app_info['name'],
                              projectType=self.app_info['application_type'],
                              status=status, code=500,
                              clusterName=self.app_info['env'],
                              message=message).update_dtalk(log_path=self.logs_path, log_info=error_info)
        else:
            service_image = imageInfo.objects.filter(name=self.app_info['name'], application_type=self.app_info[
                'application_type']).values().first()
            registry = registryInfo.objects.filter(env='test').values().first()
            if service_image is None:
                data = imageInfo.objects.create(
                    name=self.app_info['name'],
                    application_type=self.app_info['application_type'],
                    tag=self.image_tag,
                    test=f"{registry['registry']}/{registry['namespace']}/{self.app_info['name']}"
                )
                data.save()
            else:
                imageInfo.objects.filter(name=self.app_info['name'],
                                         application_type=self.app_info['application_type']).update(tag=self.image_tag)
                self.C.remove_image(image=f"{image}:{self.image_tag}")
                message, status, error_info = "部署成功", True, '[INFO] 部署成功\n'
                return update_log(projectName=self.app_info['name'],
                                  projectType=self.app_info['application_type'],
                                  status=status, code=200,
                                  clusterName=self.app_info['env'],
                                  message=message).update_dtalk(log_path=self.logs_path, log_info=error_info)


class DeployCollectionLogs:
    # 读取服务构建日志
    def __init__(self, app_info, *args, **kwargs):
        self.app_info = app_info
        self.logs_path = f'/opt/data/logs/{self.app_info["env"]}'

    def read_logs(self):
        logs_res = []
        if os.path.exists(f"{self.logs_path}/{self.app_info['name']}.log") is True:
            with open(f"{self.logs_path}/{self.app_info['name']}.log", encoding='utf-8') as f:
                for log in reversed(f.readlines()):
                    logs_res.append(log)
        return {'code': 200, 'msg': 'success', 'results': logs_res}
